package com.compression.tool.util;


import com.compression.tool.model.task.FileEntry;
import com.github.junrar.Archive;
import com.github.junrar.rarfile.FileHeader;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;

import javax.swing.tree.DefaultMutableTreeNode;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * @author 自然醒
 * @version 1.0
 */

/**
 * 预览文件的工具类
 */
public class PreviewUtils {
    private static final Logger logger = Logger.getLogger(PreviewUtils.class.getName());
    //压缩条件目录缓存 只缓存文件节点，不缓存目录节点
    public static final Map<String, Object> compressedFileCache = new HashMap<>();
//    /**
//     * 生成文件树形结构
//     * @param files
//     * @return
//     */
//    public static String generateFileTree(List<File> files) {
//        StringBuffer bufferTree = new StringBuffer();
//        for (File file : files) {
//            generateFileTree(file,0, bufferTree);
//        }
//        return bufferTree.toString();
//    }
//
//    private static void generateFileTree(File file, int depth, StringBuffer bufferTree) {
//        for (int i = 0; i < depth; i++) {
//            bufferTree.append("  ");
//        }
//        if(file.isDirectory()){
//            bufferTree.append("+ ").append(file.getName()).append("\n");
//            File[] childrenFiles = file.listFiles();
//            if (childrenFiles != null) {
//                for (File childFile : childrenFiles) {
//                    generateFileTree(childFile, depth + 1, bufferTree);
//                }
//            }
//        }else{
//            bufferTree.append("- ").append(file.getName())
//                    .append(" (").append(FileUtils.formatFileSize(file.length())).append(")\n");
//        }
//    }

    /**
     * 构建压缩文件树形结构
     *
     * @param file
     * @return
     */
    public static DefaultMutableTreeNode buildCompressedFileTree(File file) {
        //创建根节点
        DefaultMutableTreeNode root = new DefaultMutableTreeNode(new FileEntry(file.getName(), "archive", file.length()));
        try {
            String filename = file.getName().toLowerCase();
            if (filename.endsWith(".zip")) {
                buildZipFileTree(file, root);
            } else if (filename.endsWith(".rar")) {
                buildRarFileTree(file, root);
            } else if (filename.endsWith(".7z")) {
                build7zFileTree(file, root);
            } else if (filename.endsWith(".gz") || filename.endsWith(".taz")) {
                buildGzFileTree(file, root);
            }
        } catch (Exception e) {
            logger.warning("构建文件树失败: " + e.getMessage());
        }
        return root;
    }

    /**
     * 构建zip文件树形结构
     *
     * @param zipFile
     * @param node
     */
    private static void buildZipFileTree(File zipFile, DefaultMutableTreeNode node) {
        try (ZipFile zip = new ZipFile(zipFile)) {
            Enumeration<? extends ZipEntry> entries = zip.entries();
            //遍历所有条目
            while (entries.hasMoreElements()) {
                //获取条目与条目名称
                ZipEntry entry = entries.nextElement();
                String entryName = entry.getName();
                //创建节点路径 folder1/subfolder/file.txt
                String[] pathComponents = entryName.split("/");
                //创建父节点 传入的节点为根节点
                DefaultMutableTreeNode parentNode = node;
                //遍历所有节点路径，查看是否存在子节点并且进行创建
                for (int i = 0; i < pathComponents.length; i++) {
                    String component = pathComponents[i];
                    // 跳过空组件（处理以/开头或结尾的路径）
                    if (component.isEmpty()) {
                        continue;
                    }
                    // 判断是否为叶子节点：最后一个组件 + 不是目录
                    boolean isLeaf = (i == pathComponents.length - 1) && !entry.isDirectory();
                    DefaultMutableTreeNode childNode = findChild(parentNode, component);
                    if (childNode == null) {
                        FileEntry fileEntry;
                        if (isLeaf) {
                            // 文件
                            fileEntry = new FileEntry(component, "file", entry.getSize());
                            //缓存条目信息，后续进行提取
                            compressedFileCache.put(entryName, entry);
                        } else {
                            // 目录
                            fileEntry = new FileEntry(component, "directory", 0);
                        }
                        childNode = new DefaultMutableTreeNode(fileEntry);
                        parentNode.add(childNode);
                    }
                    // 向下移动，处理下一层级
                    parentNode = childNode;
                }
            }
        } catch (IOException e) {
            logger.warning("无法读取压缩文件: " + e.getMessage());
        }
    }

    /**
     * 构建rar文件树形结构
     *
     * @param rarFile
     * @param node
     */
    private static void buildRarFileTree(File rarFile, DefaultMutableTreeNode node) {
        try (Archive archive = new Archive(rarFile)) {
            if (archive == null || archive.isEncrypted()) {
                return;
            }
            List<FileHeader> fileHeaders = archive.getFileHeaders();
            for (FileHeader fileHeader : fileHeaders) {
                String fileName = fileHeader.getFileNameString();
                String[] pathComponents = fileName.split("/");
                DefaultMutableTreeNode parentNode = node;
                for (int i = 0; i < pathComponents.length; i++) {
                    String component = pathComponents[i];
                    if (component.isEmpty()) {
                        continue;
                    }
                    boolean isLeaf = (i == pathComponents.length - 1) && !fileHeader.isDirectory();
                    DefaultMutableTreeNode child = findChild(parentNode, component);
                    if (child == null) {
                        FileEntry fileEntry;
                        fileEntry = isLeaf ? new FileEntry(component, "file", fileHeader.getFullUnpackSize())
                                : new FileEntry(component, "directory", 0);
                        compressedFileCache.put(fileName, fileHeader);
                        child = new DefaultMutableTreeNode(fileEntry);
                        parentNode.add(child);
                    }
                    parentNode = child;
                }
            }
        } catch (Exception e) {
            logger.warning("构建rar文件树失败：" + e.getMessage());
        }
    }

    /**
     * 构建7z文件树形结构
     *
     * @param sevenZFile
     * @param node
     */
    private static void build7zFileTree(File sevenZFile, DefaultMutableTreeNode node) {
        try (SevenZFile sZFile = new SevenZFile(sevenZFile)) {
            SevenZArchiveEntry entry;
            while ((entry = sZFile.getNextEntry()) != null) {
                String entryName = entry.getName();
                String[] pathComponents = entryName.split("/");
                DefaultMutableTreeNode parentNode = node;
                for (int i = 0; i < pathComponents.length; i++) {
                    String component = pathComponents[i];
                    if (component.isEmpty()) {
                        continue;
                    }
                    boolean isLeaf = (i == pathComponents.length - 1) && !entry.isDirectory();
                    DefaultMutableTreeNode child = findChild(parentNode, component);
                    if (child == null) {
                        FileEntry fileEntry;
                        if(isLeaf){
                            fileEntry = new FileEntry(component, "file", entry.getSize());
                            compressedFileCache.put(entryName, entry);
                        }else{
                            fileEntry = new FileEntry(component, "directory", 0);
                        }
                        child = new DefaultMutableTreeNode(fileEntry);
                        parentNode.add(child);
                    }
                    parentNode = child;
                }
            }
        } catch (Exception e) {
            logger.warning("构建7z文件树失败：" + e.getMessage());
        }
    }

    /**
     * 构建gzip文件树形结构
     *
     * @param gzFile
     * @param node
     */
    private static void buildGzFileTree(File gzFile, DefaultMutableTreeNode node) {
        String fileName = gzFile.getName();
        if(fileName.toLowerCase().endsWith(".gz")){
            fileName = fileName.substring(0, fileName.length() - 3);
        } else if (fileName.toLowerCase().endsWith(".tgz")) {
            fileName = fileName.substring(0, fileName.length() - 4) + ".tar";
        }
        DefaultMutableTreeNode child = new DefaultMutableTreeNode(new FileEntry(fileName, "file", gzFile.length()));
        node.add(child);
        compressedFileCache.put(fileName, gzFile);
    }

    /**
     * //todo
     * 构建tar文件树形结构
     * @param tarFile
     * @param node
     */
    private static void buildTarFileTree(File tarFile, DefaultMutableTreeNode node) {
    }

    /**
     * 在父节点中查找子节点 确保相同路径的节点不会重复创建
     */
    private static DefaultMutableTreeNode findChild(DefaultMutableTreeNode parent, String name) {
        for (int i = 0; i < parent.getChildCount(); i++) {
            DefaultMutableTreeNode child = (DefaultMutableTreeNode) parent.getChildAt(i);
            FileEntry entry = (FileEntry) child.getUserObject();
            if (entry.getName().equals(name)) {
                return child;
            }
        }
        return null;
    }

    /**
     * 生成压缩文件树形结构
     *
     * @param comressedFile
     * @return 压缩文件树形结构
     */
    public static String generateCompressedFileTree(File comressedFile) {
        StringBuffer bufferTree = new StringBuffer();
        bufferTree.append("压缩文件：").append(comressedFile.getName())
                .append(" (").append(FileUtils.formatFileSize(comressedFile.length()))
                .append(")\n\n");
        try {
            if (comressedFile.getName().toLowerCase().endsWith(".zip")) {
                generateZipFileTree(comressedFile, bufferTree);
            } else if (comressedFile.getName().toLowerCase().endsWith(".rar")) {
                generateRarFileTree(comressedFile, bufferTree);
            } else if (comressedFile.getName().toLowerCase().endsWith(".7z")) {
                generate7zFileTree(comressedFile, bufferTree);
            } else if (comressedFile.getName().toLowerCase().endsWith(".gz")) {
                generateGzFileTree(comressedFile, bufferTree);
            }
        } catch (Exception e) {
            bufferTree.append("读取压缩文件时出错：").append(e.getMessage());
            logger.warning(
                    "无法读取压缩文件：" + e.getMessage()
            );
        }
        return bufferTree.toString();
    }

    /**
     * 生成zip文件树形结构
     *
     * @param zipFile
     * @param bufferTree
     * @throws Exception
     */
    private static void generateZipFileTree(File zipFile, StringBuffer bufferTree) throws Exception {
        try (ZipFile zip = new ZipFile(zipFile)) {
            //使用枚举获取zip文件项
            Enumeration<? extends ZipEntry> entries = zip.entries();
            //遍历zip文件项
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                String entryName = entry.getName();
                //计算深度(根据路径分隔符)
                int depth = countDepth(entryName);
                //进行缩进
                for (int i = 0; i < depth; i++) {
                    bufferTree.append("  ");
                }
                if (entry.isDirectory()) {
                    bufferTree.append("+ ")
                            .append(entryName)
                            .append("/\n");
                } else {
                    bufferTree.append("- ")
                            .append(getLastPathComponent(entryName))
                            .append(" (")
                            .append(FileUtils.formatFileSize(entry.getSize()))
                            .append(")\n");
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("读取zip文件失败：", e);
        }
    }

    /**
     * 生成rar文件树形结构 对于rar文件需要引入第三方库 我的版本为0.7 比较的低版本了 可以尝试引入高版本
     *
     * @param rarFile
     * @param bufferTree
     * @throws Exception
     */
    private static void generateRarFileTree(File rarFile, StringBuffer bufferTree) throws Exception {
        try (Archive archive = new Archive(rarFile)) {
            if (archive == null) {
                bufferTree.append("无法读取rar文件，可能版本过低或者文件损坏\n");
                return;
            }
            if (archive.isEncrypted()) {
                bufferTree.append("rar文件被加密，无法读取\n");
                return;
            }
            List<FileHeader> fileHeaders = archive.getFileHeaders();
            for (FileHeader fileHeader : fileHeaders) {
                String fileName = fileHeader.getFileNameString();
                int depth = countDepth(fileName);
                for (int i = 0; i < depth; i++) {
                    bufferTree.append("  ");
                }
                if (fileHeader.isDirectory()) {
                    bufferTree.append("+ ")
                            .append(fileName)
                            .append("/\n");
                } else {
                    bufferTree.append("- ")
                            .append(getLastPathComponent(fileName))
                            .append(" (")
                            .append(FileUtils.formatFileSize(fileHeader.getFullUnpackSize()))
                            .append(")\n");
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("读取rar文件失败：", e);
        }
    }

    /**
     * 生成7z文件树形结构
     *
     * @param sevenZFile
     * @param bufferTree
     * @throws Exception
     */
    private static void generate7zFileTree(File sevenZFile, StringBuffer bufferTree) throws Exception {
        try (SevenZFile sevenFile = new SevenZFile(sevenZFile)) {
            SevenZArchiveEntry entry;
            while ((entry = sevenFile.getNextEntry()) != null) {
                String entryName = entry.getName();
                int depth = countDepth(entryName);
                for (int i = 0; i < depth; i++) {
                    bufferTree.append("  ");
                }
                if (entry.isDirectory()) {
                    bufferTree.append("+ ")
                            .append(entryName)
                            .append("/\n");
                } else {
                    bufferTree.append("- ")
                            .append(getLastPathComponent(entryName))
                            .append(" (")
                            .append(FileUtils.formatFileSize(entry.getSize()))
                            .append(")\n");
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("读取7z文件失败：", e);
        }
    }

    /**
     * 生成gz文件树形结构 这种压缩格式文件一般只包含一个文件
     *
     * @param gzFile
     * @param bufferTree
     * @throws Exception
     */
    private static void generateGzFileTree(File gzFile, StringBuffer bufferTree) throws Exception {
        try (GzipCompressorInputStream gzipInputStream = new GzipCompressorInputStream(new FileInputStream(gzFile))) {
            String name = gzFile.getName();
            if (name.toLowerCase().endsWith(".gz")) {
                name = name.substring(0, name.length() - 3);
            } else if (name.toLowerCase().endsWith("taz")) {
                name = name.substring(0, name.length() - 4) + ".tar";
            }
            bufferTree.append("- ")
                    .append(name)
                    .append(" (")
                    .append(FileUtils.formatFileSize(gzFile.length()))
                    .append(")\n");
            if (name.toLowerCase().endsWith(".taz")) {
                bufferTree.append(" (这是TAR归档的GZIP文件，解压后需要进一步压缩)\n");
            } else {
                bufferTree.append(" (这是GZIP文件)\n");
            }
        } catch (Exception e) {
            throw new RuntimeException("读取gz文件失败：", e);
        }
    }

    /**
     * 计算深度
     *
     * @param path
     * @return 深度
     */
    private static int countDepth(String path) {
        return path.split("/").length - 1;
    }

    /**
     * 获取路径的最后一部分
     *
     * @param path
     * @return
     */
    private static String getLastPathComponent(String path) {
        String[] pathComponents = path.split("/");
        return pathComponents[pathComponents.length - 1];
    }


    /**
     * 获取文件图标名称
     *
     * @param file
     * @return 文件图标名称
     */
    public static String getFileTypeIcon(File file) {
        String fileType = FileUtils.detectFile(file);
        switch (fileType) {
            case "text":
                return "text-file";
            case "image":
                return "image-file";
            case "pdf":
                return "pdf-file";
            case "word":
                return "word-file";
            case "excel":
                return "excel-file";
            case "powerpoint":
                return "powerpoint-file";
            case "archive":
                return "archive-file";
            case "audio":
                return "audio-file";
            case "video":
                return "video-file";
            default:
                return "default-file";
        }
    }
}
